package com.liqi.monitor.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.liqi.common.core.domain.R;
import com.liqi.common.exception.base.BaseException;
import com.liqi.common.utils.SecurityUtils;
import com.liqi.common.utils.StringUtils;
import com.liqi.common.utils.mqtt.MqttConfig;
import com.liqi.common.utils.mqtt.MqttPushClient;
import com.liqi.common.utils.uuid.GenerateCodeUtils;
import com.liqi.deployment.domain.DvDeployment;
import com.liqi.deployment.service.IDvDeploymentService;
import com.liqi.monitor.domain.DvUpdateList;
import com.liqi.monitor.domain.DvUpdatePlan;
import com.liqi.monitor.domain.dto.*;
import com.liqi.monitor.module.DvUpdatePlanModule;
import com.liqi.monitor.module.ModuleAndDriveModule;
import com.liqi.monitor.service.DvUpdateListService;
import com.liqi.sms.utils.SmartHttp;
import com.liqi.system.service.ISysConfigService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.validation.constraints.NotBlank;

import com.liqi.monitor.mapper.DvUpdatePlanMapper;
import com.liqi.monitor.service.DvUpdatePlanService;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * @Author: lgh
 * @Date: 2022-10-27-10:54
 * @Description: 
 */
@Service
@Slf4j
public class DvUpdatePlanServiceImpl extends ServiceImpl<DvUpdatePlanMapper, DvUpdatePlan> implements DvUpdatePlanService{


    @Autowired
    private MqttConfig mqttConfig;

    // 编程式事务
    @Autowired
    private TransactionTemplate transactionTemplate;

    @Autowired
    private ThreadPoolTaskExecutor poolTaskExecutor;

    @Resource
    private DvUpdatePlanMapper dvUpdatePlanMapper;

    @Autowired
    private DvUpdateListService dvUpdateListService;

    @Autowired
    private IDvDeploymentService deploymentService;

    @Autowired
    private ISysConfigService configService;

    @Value(value = "${remote.port}")
    private Integer prot;

    @Override
    public List<ModuleAndDriveModule> getModuleAndDrive(String deviceCode) {
        return dvUpdatePlanMapper.getModuleAndDrive(deviceCode);
    }

    @Override
    public void createUpdatePlan(List<UpdatePlanDto> list) {
        // 获取请求超时时间
        String postHttpTimeOut = configService.selectConfigByKey("postHttpTimeOut");
        Integer timeOut = Integer.parseInt(postHttpTimeOut);
        //首先插入计划表数据库  408 为超时
        String planId = GenerateCodeUtils.getSequnce();
        // todo 这里需要登录的先注释掉
        String username = SecurityUtils.getUsername();
        Long deptId = SecurityUtils.getDeptId();
        Long userId = SecurityUtils.getUserId();
        List<DvUpdatePlan> dvUpdatePlans = list.stream().map(m -> {
            DvUpdatePlan dvup = new DvUpdatePlan();
            dvup.setCreateBy(username);
            dvup.setCreateTime(new Date());
            dvup.setMiddlewareNewVersion("");
            dvup.setMiddlewareOldVersion("");
            dvup.setCreateUserId(userId.toString());
            dvup.setDeptId(deptId.toString());
            dvup.setDeviceId(m.getDeviceId());
            dvup.setErrorMessage("");
            dvup.setExcecutionTime(m.getExecutionTime());
            dvup.setIssuedErrrorMessage("");
            dvup.setIssuedStatus(0);
            dvup.setStatus(0);
            dvup.setPlanId(planId);
            dvup.setPlanStatus(0);
            return dvup;
        }).collect(Collectors.toList());
        // 详细表数据
        List<DvUpdateList> dvlist = new ArrayList<>();
        list.forEach(m ->{
           List<DriveAndSdkDto> dsdk = m.getUpdateList();
           dsdk.forEach(mm ->{
               DvUpdateList dul = new DvUpdateList();
               dul.setAdapterName(mm.getAdapterName());
               dul.setAdapterVersion(mm.getAdapterVersion());
               dul.setCreateBy(username);
               dul.setAdapterLastVersion("");
               dul.setCreateTime(new Date());
               dul.setDeviceId(m.getDeviceId());
               dul.setDriveCode(mm.getDriveCode());
               dul.setDriveName(mm.getDriveName());
               dul.setOperationErrorMessage("");
               dul.setOperationResult(0);
               dul.setPlanId(planId);
               dul.setStatus(0);
               dvlist.add(dul);
           });
        });
        // 编程式事务开启
        transactionTemplate.executeWithoutResult(new Consumer<TransactionStatus>() {
            @Override
            public void accept(TransactionStatus transactionStatus) {
                // 更新计划入库
                saveBatch(dvUpdatePlans);
                // 详细信息入库
                dvUpdateListService.saveBatch(dvlist);
            }
        });


        // 发送给中间件
        list.forEach(post -> {
            CompletableFuture.runAsync(new Runnable() {
                @Override
                public void run() {
                    log.error("进入更新计划异步处理run()方法::::");
                    // 获取部署ip地址
                    DvDeployment dvDeployment = deploymentService.selectDvDeploymentByDeviceId(post.getDeviceId());
                    String ipAddress = null;
                    try {
                        ipAddress = dvDeployment.getIpAddress();
                    } catch (NullPointerException e) {
                        log.error("进入更新计划异步处理 NullPointerException::::");
                        throw new BaseException("部署IP地址为空，请联系管理员");
                    }
                    post.setCommandType(7);
                    post.setMessageCode(GenerateCodeUtils.getSequnce());
                    post.setPlanId(planId);
                    String deviceId = post.getDeviceId();
                    String json = JSON.toJSONStringWithDateFormat(post,"yyyy-MM-dd HH:mm:ss",SerializerFeature.WriteMapNullValue);
                    String finalIpAddress = ipAddress;
                    try {
                        log.error("进入更新计划异步处理 发送HTTP请求::::planId::{}:::deviceId::{}",post.getPlanId(),post.getDeviceId());
                        // 使用mqtt协议进行通信
                        MqttPushClient client = mqttConfig.getDeviceIdMqttPushClient(deviceId);
                        R publish = client.publish(0, false, deviceId, json);
                        if (!publish.isSuccess()){
                            throw new RuntimeException("更新计划消息推送失败异常");
                        }
//                        String s = SmartHttp.sendPostJsonTimeOut("http://"+ finalIpAddress +":"+prot+"/updateSoftware", json, null, timeOut);
//                        String s = SmartHttp.sendPostJsonTimeOut("http://"+"localhost:8080/monitor/updatePlan/test", json, null, timeOut);
                        // todo 这里要修改更新计划表的状态
                        // 发送成功
                        UpdateWrapper<DvUpdatePlan> uw = new UpdateWrapper<>();
                        uw.set("issued_status", 1).set("issued_time", new Date()).eq("plan_id",planId).eq("device_id",post.getDeviceId());
                        update(uw);
                        log.error("sql 成功执行 进入更新计划异步处理 发送HTTP请求成功::::planId::{}:::deviceId::{}",post.getPlanId(),post.getDeviceId());
                    } catch (Exception e) {
                        log.error("失败 sql 执行开始进入更新计划异步处理 发送HTTP请求失败::::planId::{}:::deviceId::{}",post.getPlanId(),post.getDeviceId());
                        log.info("更新计划推送到中间件失败::::{}",e.getMessage());
                        // todo 这里要修改更新计划表的状态
                        // 发送失败
                        UpdateWrapper<DvUpdatePlan> uw = new UpdateWrapper<>();
                        uw.set("issued_status", -1).set("issued_time", new Date()).set("issued_errror_message",e.getMessage()).eq("plan_id",planId).eq("device_id",post.getDeviceId());
                        update(uw);
                        log.error("sql 失败 执行结束 进入更新计划异步处理 发送HTTP请求失败::::planId::{}:::deviceId::{}",post.getPlanId(),post.getDeviceId());
                    }
                }
            },poolTaskExecutor);
        });
    }

    @Override
    @Transactional
    public void saveUpdateSoftwareResult(UpdateResultDto resultDto) {
        // 1.修改设备对应更新计划状态
        String planId = resultDto.getPlanId().trim();
        String deviceId = resultDto.getDeviceId().trim();
        UpdateWrapper<DvUpdatePlan> uw = new UpdateWrapper<>();
        uw.set("plan_status", resultDto.getPlanStatus()).set("error_message", resultDto.getErrorMessage())
                .set("middleware_old_version",resultDto.getMiddlewareOldVersion())
                .set("middleware_new_version",resultDto.getMiddlewareNewVersion())
                .eq("plan_id", planId).eq("device_id", deviceId);
        update(uw);
        // 2.更新详细列表
        List<DriveAndSdkDto> resultDetail = resultDto.getResultDetail();
        // todo mybatis报错，sql 可以执行
//        dvUpdatePlanMapper.saveUpdateSoftwareResult(planId, deviceId, resultDetail);
        resultDetail.forEach(m ->{
            UpdateWrapper<DvUpdateList> upw = new UpdateWrapper<>();
            if ("1".equals(m.getOperationResult())) {
                upw.set("operation_result", m.getOperationResult()).set("operation_error_message", StringUtils.isEmpty(m.getOperationErrorMessage()) ? " " : m.getOperationErrorMessage())
                        .set("adapter_last_version", m.getAdapterLastVersion())
                        .set("adapter_version",m.getAdapterCurrentVersion())
                        .eq("plan_id", planId).eq("device_id", deviceId)
                        .eq("drive_code", m.getDriveCode()).eq("adapter_name", m.getAdapterName());
            }else {
                upw.set("operation_result", m.getOperationResult()).set("operation_error_message", StringUtils.isEmpty(m.getOperationErrorMessage()) ? " " : m.getOperationErrorMessage())
                        .eq("plan_id", planId).eq("device_id", deviceId)
                        .eq("drive_code", m.getDriveCode()).eq("adapter_name", m.getAdapterName());
            }
            dvUpdateListService.update(upw);
        });
    }

    @Override
    public List<DvUpdatePlanModule> planList(Page<DvUpdatePlanModule> page, PlanListDto dto) {
        List<DvUpdatePlanModule> dvUpdatePlans = dvUpdatePlanMapper.planList(page,dto);
        return dvUpdatePlans;
    }

//    @Override
//    public void stopUpdateSoftware(List<DeviceInfoDto> deviceInfoDto) {
//        // 获取请求超时时间
//        String postHttpTimeOut = configService.selectConfigByKey("postHttpTimeOut");
//        Integer timeOut = Integer.parseInt(postHttpTimeOut);
//        deviceInfoDto.forEach(m ->{
//            CompletableFuture.runAsync(new Runnable() {
//                @Override
//                public void run() {
//                    log.error("进入终止计划开始:::::planId:{}::deviceId:{}",m.getPlanId(),m.getDeviceId());
//// 先修改数据库终止时间然后 在调用中间件接口
//                    update(new UpdateWrapper<DvUpdatePlan>().set("stop_time", new Date()).eq("plan_id", m.getPlanId()).eq("device_id", m.getDeviceId()));
//                    DvDeployment dvDeployment = deploymentService.selectDvDeploymentByDeviceId(m.getDeviceId());
//                    try {
//                        // todo 这里后面要修改为真实的中间件地址
//                        String json = SmartHttp.sendPostJsonTimeOut("http://" + dvDeployment.getIpAddress() + ":"+prot+"/stopUpdateSoftware", JSON.toJSONStringWithDateFormat(m,"yyyy-MM-dd HH:mm:ss", SerializerFeature.WriteMapNullValue), null, timeOut);
////                        String json = SmartHttp.sendPostJsonTimeOut("http://localhost:8080" + "/monitor/updatePlan/test" , JSON.toJSONString(m), hashMap, timeOut);
//                        JSONObject jsonObject = JSON.parseObject(json);
//                        JSONObject data = jsonObject.getJSONObject("data");
//                        String planStatus = data.getString("planStatus");
//                        String errorMessage = data.getString("errorMessage");
//                        update(new UpdateWrapper<DvUpdatePlan>().set("plan_status", planStatus)
//                                .set("error_message", StringUtils.isEmpty(errorMessage) ? " " : errorMessage)
//                                .eq("plan_id", m.getPlanId()).eq("device_id", m.getDeviceId()));
//                        log.error("进入终止计划成功:::::planId:{}::deviceId:{}",m.getPlanId(),m.getDeviceId());
//                    } catch (Exception e) {
//                        update(new UpdateWrapper<DvUpdatePlan>().set("plan_status", "-2").set("error_message", "终止指令下发失败错误，请联系管理员"+e.getMessage()).eq("plan_id", m.getPlanId()).eq("device_id", m.getDeviceId()));
//                        log.info("终止计划命令下发失败:::{}", e.getMessage());
//                        log.error("进入终止计划失败:::::planId:{}::deviceId:{}",m.getPlanId(),m.getDeviceId());
//                    }
//                }
//            }, poolTaskExecutor);
//        });
//    }

    @Override
    public String stopUpdateSoftware(List<DeviceInfoDto> deviceInfoDto) {
        String messageCode = null;
        for(DeviceInfoDto m : deviceInfoDto){
            log.error("进入终止计划开始:::::planId:{}::deviceId:{}", m.getPlanId(), m.getDeviceId());
            // 先修改数据库终止时间然后 在调用中间件接口
            update(new UpdateWrapper<DvUpdatePlan>().set("stop_time", new Date()).eq("plan_id", m.getPlanId()).eq("device_id", m.getDeviceId()));
            DvDeployment dvDeployment = deploymentService.selectDvDeploymentByDeviceId(m.getDeviceId());
            try {
                m.setCommandType(8);
                messageCode = GenerateCodeUtils.getSequnce();
                m.setMessageCode(messageCode);
                // todo mqtt协议改造
                String sentJson = JSON.toJSONStringWithDateFormat(m, "yyyy-MM-dd HH:mm:ss", SerializerFeature.WriteMapNullValue);
                MqttPushClient client = mqttConfig.getDeviceIdMqttPushClient(m.getDeviceId());
                R publish = client.publish(0, false, m.getDeviceId(), sentJson);
                if (!publish.isSuccess()) {
                    throw new RuntimeException("更新计划消息推送失败异常");
                }
            } catch (Exception e) {
                update(new UpdateWrapper<DvUpdatePlan>().set("plan_status", "-2").set("error_message", "终止指令下发失败错误，请联系管理员" + e.getMessage()).eq("plan_id", m.getPlanId()).eq("device_id", m.getDeviceId()));
                log.info("终止计划命令下发失败:::{}", e.getMessage());
                log.error("进入终止计划失败:::::planId:{}::deviceId:{}", m.getPlanId(), m.getDeviceId());
            }
        }
        return messageCode;
    }

    @Override
    public Integer getStatusBymessageCode(String msgCode) {
        return dvUpdatePlanMapper.getStatusBymessageCode(msgCode);
    }

}
